﻿using UnityEngine;
using System.Collections;
using LuaInterface;

namespace LuaFramework {
    public class LuaManager : MonoBehaviour {
        private static LuaManager m_instance;
        public static LuaManager Instance{
            get{ 
                if (m_instance == null) {
                    GameObject go = new GameObject ();
                    m_instance = go.AddComponent<LuaManager> ();
                }
                return m_instance;
            }
        }
        public static bool IsInited{
            get{ 
                return m_instance != null;
            }
        }

        private LuaState _lua;
        private LuaLoader _loader;
        private LuaLooper _loop = null;

        public LuaState Lua{
            get{
                return _lua;
            }
        }

        // Use this for initialization
        void Awake() {
            if (m_instance != null) {
                Destroy (gameObject);
                return;
            }

            _loader = new LuaLoader();
            _lua = new LuaState();
            this.OpenLibs();
            _lua.LuaSetTop(0);

            LuaBinder.Bind(_lua);
            DelegateFactory.Init();
            LuaCoroutine.Register(_lua, this);

            DontDestroyOnLoad(gameObject);
            m_instance = this;
            name = "[LuaManager]";

            InitStart();


            UnityEngine.SceneManagement.SceneManager.sceneLoaded += delegate(UnityEngine.SceneManagement.Scene scene, UnityEngine.SceneManagement.LoadSceneMode mode) {
                if(mode== UnityEngine.SceneManagement.LoadSceneMode.Single){
                    LuaFunction func = _lua.GetFunction("OnLevelWasLoaded");
                    if(func!=null){
                        func.BeginPCall();
                        func.Push(scene.name);
                        func.PCall();
                        func.EndPCall();
                        func.Dispose();
                        func = null;
                    }
                }
            };
        }

        void InitStart() {
            InitLuaPath();
            InitLuaBundle();
            this._lua.Start();    //启动LUAVM
            this.LoadRect();
            this.StartMain();
            this.StartLooper();
        }

        void StartLooper() {
            _loop = gameObject.AddComponent<LuaLooper>();
            _loop.luaState = _lua;
        }

        //cjson 比较特殊，只new了一个table，没有注册库，这里注册一下
        protected void OpenCJson() {
            _lua.LuaGetField(LuaIndexes.LUA_REGISTRYINDEX, "_LOADED");
            _lua.OpenLibs(LuaDLL.luaopen_cjson);
            _lua.LuaSetField(-2, "cjson");

            _lua.OpenLibs(LuaDLL.luaopen_cjson_safe);
            _lua.LuaSetField(-2, "cjson.safe");
        }

        void StartMain() {
            _lua.DoFile("Comm/Main.lua");

            LuaFunction main = _lua.GetFunction("Main");
            if (main != null)
            {
                main.Call();
                main.Dispose();
                main = null;    
            }
        }

        
        /// <summary>
        /// 初始化加载第三方库
        /// </summary>
        void OpenLibs() {
//            lua.OpenLibs(LuaDLL.luaopen_pb);      
//            lua.OpenLibs(LuaDLL.luaopen_sproto_core);
//            lua.OpenLibs(LuaDLL.luaopen_protobuf_c);
//            lua.OpenLibs(LuaDLL.luaopen_lpeg);
//            lua.OpenLibs(LuaDLL.luaopen_bit);
//            lua.OpenLibs(LuaDLL.luaopen_socket_core);
            this.OpenCJson();
        }





        #region Rect
        LuaFunction NewRect = null;
        LuaFunction GetRect = null; 

        void LoadRect(){
            DoFile("UnityEngine/Rect");

            NewRect = _lua.GetFunction("Rect.New");
            GetRect = _lua.GetFunction("Rect.Get");
            StackTraits<Rect>.Init(PushRect, CheckRectValue, ToRectValue);           //支持压入lua以及从lua栈读取
            TypeTraits<Rect>.Init(CheckRectType);                                    //支持重载函数TypeCheck.CheckTypes
            TypeTraits<System.Nullable<Rect>>.Init(CheckNullRectType);                      //支持重载函数TypeCheck.CheckTypes
            LuaValueTypeName.names[13] = "Rect";                      //CheckType失败提示的名字
            TypeChecker.LuaValueTypeMap[13] = typeof(Rect);           //用于支持类型匹配检查操作
            ToLua.ToVarMap[13] = ToRectTable;                         //Rect作为object读取
            ToLua.VarPushMap[typeof(Rect)] = (L, o) => { PushRect(L, (Rect)o); };    //Rect作为object压入

        }

        void PushRect(System.IntPtr L, Rect rt)
        {
            LuaDLL.lua_getref(L, NewRect.GetReference());
            LuaDLL.lua_pushnumber(L, rt.xMin);
            LuaDLL.lua_pushnumber(L, rt.yMin);
            LuaDLL.lua_pushnumber(L, rt.width);
            LuaDLL.lua_pushnumber(L, rt.height);
            LuaDLL.lua_call(L, 4, 1);
        }

        Rect ToRectValue(System.IntPtr L, int pos)
        {
            pos = LuaDLL.abs_index(L, pos);
            LuaDLL.lua_getref(L, GetRect.GetReference());
            LuaDLL.lua_pushvalue(L, pos);
            LuaDLL.lua_call(L, 1, 4);
            float x = (float)LuaDLL.lua_tonumber(L, -4);
            float y = (float)LuaDLL.lua_tonumber(L, -3);
            float w = (float)LuaDLL.lua_tonumber(L, -2);
            float h = (float)LuaDLL.lua_tonumber(L, -1);
            LuaDLL.lua_pop(L, 4);

            return new Rect(x, y, w, h);
        }

        Rect CheckRectValue(System.IntPtr L, int pos)
        {
            int type = LuaDLL.tolua_getvaluetype(L, pos);

            if (type != 13)
            {
                _lua.LuaTypeError(pos, "Rect", LuaValueTypeName.Get(type));
                return new Rect();
            }

            return ToRectValue(L, pos);
        }

        bool CheckRectType(System.IntPtr L, int pos)
        {
            return LuaDLL.tolua_getvaluetype(L, pos) == 13;
        }

        bool CheckNullRectType(System.IntPtr L, int pos)
        {
            LuaTypes luaType = LuaDLL.lua_type(L, pos);

            switch (luaType)
            {
                case LuaTypes.LUA_TNIL:
                    return true;
                case LuaTypes.LUA_TTABLE:
                    return LuaDLL.tolua_getvaluetype(L, pos) == 13;
                default:
                    return false;
            }
        }

        object ToRectTable(System.IntPtr L, int pos)
        {
            return ToRectValue(L, pos);
        }

        #endregion








        /// <summary>
        /// 初始化Lua代码加载路径
        /// </summary>
        void InitLuaPath() {
            if (AppConst.DebugMode) {
                string rootPath = Application.dataPath;
                _lua.AddSearchPath(rootPath + "/Lua");
                _lua.AddSearchPath(rootPath + "/LuaFramework/ToLua/Lua");
            } else {
                _lua.AddSearchPath(Util.DataPath + "lua");
            }
        }

        /// <summary>
        /// 初始化LuaBundle
        /// </summary>
        void InitLuaBundle() {
            if (_loader.beZip) {
                _loader.AddBundle("lua/lua.unity3d");
                _loader.AddBundle("lua/lua_math.unity3d");
                _loader.AddBundle("lua/lua_system.unity3d");
                _loader.AddBundle("lua/lua_system_reflection.unity3d");
                _loader.AddBundle("lua/lua_unityengine.unity3d");
                _loader.AddBundle("lua/lua_misc.unity3d");
				_loader.AddBundle("lua/lua_cjson.unity3d");
				_loader.AddBundle("lua/lua_comm.unity3d");
            }
        }

        public void DoFile(string filename) {
            _lua.DoFile(filename);
        }

		public LuaTable RequireFile(string filename) {
			return _lua.Require<LuaTable>(filename);
		}

        public void DoLuaString(string data,string chunkName)
        {
            _lua.DoString(data, chunkName);
        }

        // Update is called once per frame
        public object[] CallFunction(string funcName, params object[] args) {
            LuaFunction func = _lua.GetFunction(funcName);
            if (func != null) {
                return func.LazyCall(args);
            }
            return null;
        }

        public void LuaGC() {
            _lua.LuaGC(LuaGCOptions.LUA_GCCOLLECT);
        }

        public void Close() {
            _loop.Destroy();
            _loop = null;

            _lua.Dispose();
            _lua = null;

			_loader.Dispose();
            _loader = null;
        }
    }
}